from flask import request, jsonify, current_app, make_response, session
from datetime import datetime
import random
import re

"""passport/view.py保存和验证有关的视图函数"""
from . import passport_blu
from info import redis_db, constants, db
from info.utils.response_code import RET
from info.utils.captcha.captcha import captcha
from info.lib.yuntongxun.sms import CCP
from info.models import User


# 3.用蓝图对象修饰视图函数
@passport_blu.route('/image_code')
def get_image_code():
    """获取验证码图片"""

    # 1.获取请求中的参数:图片编号
    code_id = request.args.get('code_id')

    # 2.判断参数是否存在
    if not code_id:
        return jsonify(errno=RET.PARAMERR, errmsg='验证码参数错误')

    # 3.调用工具包captcha生成图片验证码
    name, text, image = captcha.generate_captcha()

    # 4.保存图片编号和图片验证码对应的文字内容到redis中
    try:
        redis_db.set('ImageCode_' + code_id, text, ex=constants.IMAGE_CODE_REDIS_EXPIRES)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='保存图片验证码失败')

    # 5.设置响应内容和内容类型
    response = make_response(image)
    response.headers['Content-Type'] = 'image/jpg'

    return response


@passport_blu.route('/sms_code', methods=['POST'])
def send_sms_code():
    """
    发送短信验证码:
    1.接口设计:
        URL：/passport/sms_code
        请求方式：POST
        传入参数：JSON格式
    2.参数:
        参数名	类型	是否必须	参数说明
        mobile	string	是	手机号
        image_code	string	是	用户输入的图片验证码内容
        image_code_id	string	是	真实图片验证码编号
    3.返回类型：JSON
        参数名	类型	是否必须	参数说明
        errno	int	是	错误码
        errmsg	string	是	错误信息
    """
    # 1.接收参数:mobile 手机号,image_code 用户输入的图片验证码内容,image_code_id 真实图片验证码编号
    temp_json_dict = request.json
    mobile = temp_json_dict.get('mobile')
    image_code = temp_json_dict.get('image_code')
    image_code_id = temp_json_dict.get('image_code_id')

    # 2.判断三个参数是否有值
    if not all([mobile, image_code, image_code_id]):
        return jsonify(errno=RET.PARAMERR, errmsg='参数有误')

    # 3.判断手机号格式是否准确
    if not re.match(r'^1[3578][0-9]{9}$', mobile):
        return jsonify(errno=RET.DATAERR, errmsg='手机号码有误')

    # 4.通过图片验证码编号从redis中取出对应的图片验证码内容
    try:
        real_image_code = redis_db.get('ImageCode_' + image_code_id)

        # TODO 如果能取出来数据,删除redis中的原有数据
        # TODO 从redis中取出的数据默认是二进制,需要解码:'\x89PNG\r\n\x1a\n\x00\x00\x00\r...'
        if real_image_code:
            redis_db.delete('ImageCode_' + image_code_id)

    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='获取图片验证码失败')

    # 4.1如果从redis取出来的验证码内容不存在,表示验证码已经过期:增加判断,进行访问控制
    if not real_image_code:
        return jsonify(errno=RET.NODATA, errmsg='图片验证码已经过期')

    # 4.2对比传过来的image_code和从redis中取出的值,如果不相等则返回错误信息
    if image_code.upper() != real_image_code.upper():
        return jsonify(errno=RET.DATAERR, errmsg='图片验证码输入错误')

    # 5.所有条件都符合则发送短信验证码,随机生成6位数
    random_num = random.randint(0, 999999)
    sms_code = '%06d' % random_num
    # 5.1调用info.lib.yuntongxun.sms.CCP()第三方工具发送短信验证码
    current_app.logger.debug('短信验证码为:%s' % sms_code)
    result = CCP().send_template_sms(mobile, [sms_code, constants.SMS_CODE_REDIS_EXPIRES // 60], 1)
    # 根据send_template_sms()返回值判断短信是否发送成功
    if result != 0:
        return jsonify(errno=RET.THIRDERR, errmsg='发送短信验证码失败')

    # 6.保存用户手机号和短信验证码内容到redis中
    try:
        redis_db.set('SMS_' + mobile, sms_code, ex=constants.SMS_CODE_REDIS_EXPIRES)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='保存短信验证码错误')

    # 7.返回发送成功的响应
    return jsonify(errno=RET.OK, errmsg='短信验证码发送成功')


@passport_blu.route('/register', methods=['POST'])
def register():
    """
    注册信息校验:
    1.接口设计
        URL：/passport/register
        请求方式：POST
        传入参数：JSON格式
    2.参数
        参数名	类型	是否必须	参数说明
        mobile	string	是	手机号
        sms_code	string	是	短信验证码
        password	string	是	密码
    3.返回类型：JSON
        参数名	类型	是否必须	参数说明
        errno	int	是	错误码
        errmsg	string	是	错误信息
    """
    # 1.获取参数:mobile,sms_code,password
    temp_json_dict = request.json
    mobile = temp_json_dict.get('mobile')
    sms_code = temp_json_dict.get('sms_code')
    pwd = temp_json_dict.get('password')

    # 2.参数为空校验
    if not all([mobile, sms_code, pwd]):
        return jsonify(errno=RET.PARAMERR, errmsg='参数错误')

    # 3.手机号码校验
    if not re.match(r'^1[35678]\d{9}$', mobile):
        return jsonify(errno=RET.PARAMERR, errmsg='手机号码格式有误')

    # 4.短信验证码校验
    try:
        real_sms_code = redis_db.get('SMS_' + mobile)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='数据库查询出错')
    # 4.1判断取出来的验证码是否有值
    if not real_sms_code:
        return jsonify(errno=RET.NODATA, errmsg='短信验证码已经过期')
    # 4.2删除redis中的短信验证码:清除缓存
    try:
        redis_db.delete('SMS_' + mobile)
    except Exception as e:
        current_app.logger.error(e)
    # 4.3如果取出来的real_sms_code和sms_code不相等
    if real_sms_code != sms_code:
        return jsonify(errno=RET.DATAERR, errmsg='短信验证码有误')

    # 5.将用户数据保存到mysql数据库中
    user = User()
    user.nick_name = mobile
    user.mobile = mobile
    # 5.1 TODO 加密用户密码再保存:此处的password是一个实例方法
    user.password = pwd
    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='数据库保存数据失败')

    # 5.2 TODO 将用户账号和密码写入到cookie中,用户状态保持
    session['user_id'] = user.id
    session['nick_name'] = user.nick_name
    session['mobile'] = user.mobile
    session['is_admin'] = False

    # 6.返回响应数据
    return jsonify(errno=RET.OK, errmsg='注册成功')


@passport_blu.route('/login', methods=['POST'])
def login():
    """
    登录信息校验:
    1.接口设计
        URL：/passport/login
        请求方式：POST
        传入参数：JSON格式
    2.参数
        参数名	类型	是否必须	参数说明
        mobile	string	是	手机号
        password	string	是	密码
    3.返回类型：JSON
        参数名	类型	是否必须	参数说明
        errno	int	是	错误码
        errmsg	string	是	错误信息
    """
    # 1.获取参数:mobile,password
    temp_json_dict = request.json
    mobile = temp_json_dict.get('mobile')
    pwd = temp_json_dict.get('password')

    # 2.判断参数是否为空
    if not all([mobile, pwd]):
        return jsonify(errno=RET.PARAMERR, errmsg='参数错误')
    # 2.1判断手机号是否符合规范
    if not re.match(r'^1[35678]\d{9}$', mobile):
        return jsonify(errno=RET.PARAMERR, errmsg='手机号格式有误')

    # 3.从数据库中查询mobile是否存在,密码是否正确
    try:
        user = User.query.filter(User.mobile == mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='数据库查询出错')
    if not user:
        return jsonify(errno=RET.USERERR, errmsg='用户名不存在')
    if not user.check_passowrd(pwd):
        return jsonify(errno=RET.PWDERR, errmsg='密码错误')

    # 4.验证通过,响应中写入cookie,状态保持
    session['mobile'] = user.mobile
    session['user_id'] = user.id
    session['nick_name'] = user.nick_name
    session['is_admin'] = user.is_admin if user.is_admin else False
    # 4.2记录最后一次登陆时间
    user.last_login = datetime.now()

    # 5.返回响应
    return jsonify(errno=RET.OK, errmsg='登陆成功')


@passport_blu.route('/logout', methods=['POST'])
def logout():
    """退出登录:实则清除session中保存的用户缓存信息"""
    session.pop('user_id', None)
    session.pop('nick_name', None)
    session.pop('mobile', None)
    session.pop('is_admin', None)

    return jsonify(errno=RET.OK, errmsg='退出登陆成功')
